﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Expect.Core.BaseClasses
{
    using System.Globalization;

    /// <summary>
    /// Default implementation for <see cref="INameProvider"/>
    /// </summary>
    public class DefaultNameProvider : INameProvider
    {
        /// <summary>
        /// Transform a string by making it compliant to a code name. For example, removing accents or spaces
        /// </summary>
        /// <param name="input">the input string</param>
        /// <returns>conformed string</returns>
        public string ConformString(string input)
        {
            string res = input;
            res = res.Replace("%", "Percent");
            res = res.Replace("$", "Dollars");
            res = res.Replace("€", "Euros");
            res = res.Replace("£", "Pounds");

            res = RemoveDiacritics(res);
            StringBuilder sb = new StringBuilder();
            foreach (char s in res)
            {
                if (char.IsLetterOrDigit(s))
                {
                    sb.Append(s);
                }
            }

            return sb.ToString();
        }

        /// <summary>
        /// Build an element name by concatenation of the strings and appliance of PascalCase
        /// </summary>
        /// <param name="inputs">the list of strings input</param>
        /// <returns></returns>
        public string ElementName(params string[] inputs)
        {
            System.Text.StringBuilder sb = new StringBuilder();
            foreach(string s in inputs)
            {                
                string conformed = s;
                
                conformed = GetConformed(' ', s, conformed);
                conformed = GetConformed('\'', s, conformed);
                conformed = GetConformed('-', s, conformed);

                if (s.Contains("'"))
                {
                    string[] tokenized = s.Split('\'');
                    conformed = ElementName(tokenized);
                }

                if (!string.IsNullOrEmpty(conformed))
                {
                    conformed = this.ConformString(conformed);
                    if (!string.IsNullOrEmpty(conformed))
                    {
                        sb.Append(conformed[0].ToString().ToUpper());
                        sb.Append(conformed.Substring(1));
                    }
                }
            }

            return sb.ToString();
        }

        private string GetConformed(char separator, string s, string conformed)
        {
            if (s.Contains(separator))
            {
                string[] tokenized = s.Split(separator);
                conformed = this.ElementName(tokenized);
            }
            return conformed;
        }

        /// <summary>
        /// Indicate if the input string is a method. For example fit's method name end with "()" or "?"
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public bool IsAMethod(string input)
        {
            if (input.Trim().EndsWith("?"))
                return true;

            if (input.Trim().EndsWith("()"))
                return true;

            return false;
        }

        /// <summary>
        /// Remove the method identifier of the input. ConformString may call this method...
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public string RemoveMethodIdentifier(string input)
        {
            input = input.Replace("?", "");
            input = input.Replace("()", "");
            return input;
        }

        /// <summary>
        /// Indicate if the input could be considered as a keyword of the provided type. 
        /// This method help translating keywords or at least flexibility about the keywords in fixtures like ActionFixture
        /// </summary>
        /// <param name="keyword">the keyword "type" or root definition</param>
        /// <param name="input">the input string</param>
        /// <returns>true if the input match the keyword</returns>
        public bool IsAKeywordFor(string keyword, string input)
        {
            if (keyword == input)
                return true;

            ////TODO implement a keyword mecanism instead...

            return false;
        }
        
        private static string CleanUp(string input)
        {
            while (input.Contains("--"))
            {
                input = input.Replace("--", "-");
            }

            while (input.EndsWith("-"))
            {
                input = input.Substring(0, input.Length - 1);
            }

            while (input.StartsWith("-"))
            {
                input = input.Substring(1, input.Length - 1);
            }

            return input;
        }

        /// <summary>
        /// Remplacement des caractères accentués
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string RemoveDiacritics(string s)
        {
            string normalizedString = s.Normalize(NormalizationForm.FormD);
            StringBuilder stringBuilder = new StringBuilder();

            foreach (char c in normalizedString)
            {
                if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
                    stringBuilder.Append(c);
            }

            return stringBuilder.ToString();
        }
    }
}
